﻿using Hydrogen.MathExt.Expression.Functions;
using Hydrogen.MathExt.Expression.Functions.Operators;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;

namespace Hydrogen.MathExt.Expression
{
    /// <summary>
    /// 负责预处理表达式（处理空格、负数、科学计数法等）<br/>
    /// 将表达式字符串解析为 Token 列表<br/>
    /// 处理乘号省略写法（如 "2x" 转换为 "2*x"）
    /// </summary>
    internal class ExpressionParser
    {
        private readonly static Regex _regex =
            new Regex(@"(?<function>[a-z][a-z0-9]*)(?=\()|(?<constant>pi|e)|(?<variable>[a-z][a-z0-9_]*)|(?<number>\d+\.?\d*)|(?<operator>[+\-*/%^])|(?<parenthesis>[()])|(?<comma>,)");

        public static List<Token> Tokenize(string expression)
        {
            List<Token> tokens = new List<Token>();
            string processedExpression = PreprocessExpression(expression);

            MatchCollection matches = _regex.Matches(processedExpression);

            for (int i = 0; i < matches.Count; i++)
            {
                string value = matches[i].Value;

                if (double.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out double number))
                {
                    tokens.Add(new Token(TokenType.Number, value, number));
                }
                else if (ExpressionHelper.TryGetConstants(value, out var constant))
                {
                    tokens.Add(new Token(TokenType.Constant, value, constant));
                }
                else if (FunctionBase.IsFunction(value))
                {
                    tokens.Add(new Token(TokenType.Function, value));
                }
                else if (value == ExpressionHelper.LEFT_PARENTHESIS)
                {
                    tokens.Add(new Token(TokenType.LeftParenthesis, value));
                }
                else if (value == ExpressionHelper.RIGHT_PARENTHESIS)
                {
                    tokens.Add(new Token(TokenType.RightParenthesis, value));
                }
                else if (value == ExpressionHelper.COMMA)
                {
                    tokens.Add(new Token(TokenType.Comma, value));
                }
                else if (OperatorBinaryBase.IsOperator(value))
                {
                    tokens.Add(new Token(TokenType.Operator, value));
                }
                else
                {
                    tokens.Add(new Token(TokenType.Variable, value));
                }
            }

            return tokens;
        }

        private static string PreprocessExpression(string expression)
        {
            // 移除所有空格并转换为小写
            expression = Regex.Replace(expression, @"\s+", "").ToLower();

            // 处理负数：将 "(-" 替换为 "(0-"
            expression = Regex.Replace(expression, @"\(-", "(0-");

            // 处理开头为负号的情况
            if (expression.StartsWith("-")) expression = "0" + expression;

            // 处理科学计数法
            expression = Regex.Replace(expression, @"(\d+\.?\d*)e([+-]?)(\d+)", match =>
            {
                double baseValue = double.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
                string expSign = match.Groups[2].Value;
                int exponent = int.Parse(match.Groups[3].Value);
                if (expSign == "-") exponent = -exponent;
                return (baseValue * System.Math.Pow(10, exponent)).ToString(CultureInfo.InvariantCulture);
            });

            // 处理乘号省略：在数字与字母/括号之间插入乘号
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < expression.Length; i++)
            {
                sb.Append(expression[i]);
                if (i < expression.Length - 1)
                {
                    char current = expression[i];
                    char next = expression[i + 1];

                    // 数字后跟字母或'('
                    if (char.IsDigit(current) && (char.IsLetter(next) || next == '('))
                    {
                        sb.Append('*');
                    }
                    // ')'后跟数字、字母或'('
                    else if (current == ')' && (char.IsDigit(next) || char.IsLetter(next) || next == '('))
                    {
                        sb.Append('*');
                    }
                    //// 字母后跟数字或字母
                    //else if (char.IsLetter(current) && (char.IsDigit(next)))
                    //{
                    //    sb.Append('*');
                    //}
                }
            }
            return sb.ToString();
        }
    }
}
